//Copyright (c) 2006-2007 Microsoft Corporation.  All rights reserved
using System;
using System.Diagnostics;

namespace InvocationFramework.Common.Instrumentation
{
    /// <summary>
    /// Esta clase es un helper para registrar performance counters relacionados con el Shell
    /// </summary>
    /// <author>Rodolfo Finochietti 11/10/2005 10.14</author>
    internal class PerformanceCountersManager : IDisposable
    {
        #region Factory

        /// <summary>
        /// Variable privada para el manejo de la propiedad
        /// </summary>
        private readonly static PerformanceCountersManager _instance = new PerformanceCountersManager( );

        /// <summary>
        /// Singleton para manejar una sola instancia
        /// </summary>
        public static PerformanceCountersManager Instance
        {
            get
            {
                return _instance;
            }
        }

        #endregion

        #region Constants

        private const string General = "Services Invocation Framework";

        private const string TotalName = "_Total";

        #endregion

        #region Private Vars

        private static Stopwatch _stopWatch = new Stopwatch( );
        
        private PerformanceCounter _invocationsTotal;
        private PerformanceCounter _invocationsPerSecond;
        private PerformanceCounter _averageInvocationTotal;
        private PerformanceCounter _averageInvocationTotalBase;

        #endregion

        #region Constructors

        /// <summary>
        /// Este constructor crea las categorias necesaria para los performance counters del Invocation Framework
        /// </summary>
        /// <remarks>
        /// El constructor es provado por que los clinetes de esta case solo acceden a una instancia atravez de la propiedad <seealso cref="Instance"/>
        /// </remarks>
        private PerformanceCountersManager( )
        {
            //Si no existe la categoria para los contadores generales la creo
            if ( !PerformanceCounterCategory.Exists( General ) )
            {
                CounterCreationDataCollection counters = new CounterCreationDataCollection( );

                CounterCreationData invocationsTotal = new CounterCreationData( );
                invocationsTotal.CounterName = "# of invocations";
                invocationsTotal.CounterHelp = "Total number of invocations";
                invocationsTotal.CounterType = PerformanceCounterType.NumberOfItems32;
                counters.Add( invocationsTotal );

                CounterCreationData invocationsPerSecond = new CounterCreationData( );
                invocationsPerSecond.CounterName = "# of invocations per sec";
                invocationsPerSecond.CounterHelp = "Number of invocations per second";
                invocationsPerSecond.CounterType = PerformanceCounterType.RateOfCountsPerSecond32;
                counters.Add( invocationsPerSecond );

                CounterCreationData averageInvocationTotal = new CounterCreationData( );
                averageInvocationTotal.CounterName = "average time per invocation execution";
                averageInvocationTotal.CounterHelp = "Average duration per invocation execution";
                averageInvocationTotal.CounterType = PerformanceCounterType.AverageTimer32;
                counters.Add( averageInvocationTotal );

                CounterCreationData averageInvocationTotalBase = new CounterCreationData( );
                averageInvocationTotalBase.CounterName = "average time per invocation execution base";
                averageInvocationTotalBase.CounterHelp = "Average duration per invocation execution base";
                averageInvocationTotalBase.CounterType = PerformanceCounterType.AverageBase;
                counters.Add( averageInvocationTotalBase );

                //Creo la categoria con los contadores previamente mencioandos
                PerformanceCounterCategory.Create( General, "Services Invocation Framework General Counters", PerformanceCounterCategoryType.MultiInstance, counters );
            }

            _invocationsTotal = new PerformanceCounter( );
            _invocationsTotal.CategoryName = General;
            _invocationsTotal.CounterName = "# of invocations";
            _invocationsTotal.MachineName = ".";
            _invocationsTotal.ReadOnly = false;

            _invocationsPerSecond = new PerformanceCounter( );
            _invocationsPerSecond.CategoryName = General;
            _invocationsPerSecond.CounterName = "# of invocations per sec";
            _invocationsPerSecond.MachineName = ".";
            _invocationsPerSecond.ReadOnly = false;

            _averageInvocationTotal = new PerformanceCounter( );
            _averageInvocationTotal.CategoryName = General;
            _averageInvocationTotal.CounterName = "average time per invocation execution";
            _averageInvocationTotal.MachineName = ".";
            _averageInvocationTotal.ReadOnly = false;

            _averageInvocationTotalBase = new PerformanceCounter( );
            _averageInvocationTotalBase.CategoryName = General;
            _averageInvocationTotalBase.CounterName = "average time per invocation execution base";
            _averageInvocationTotalBase.MachineName = ".";
            _averageInvocationTotalBase.ReadOnly = false;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Variable privada para el manejo de la propiedad
        /// </summary>
        private bool _enablePerformanceCounters;

        /// <summary>
        /// Indica si publican o no los performance counters
        /// </summary>
        public bool EnablePerformanceCounters
        {
            get
            {
                return _enablePerformanceCounters;
            }
            set
            {
                _enablePerformanceCounters = value;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Incrementa los contadores de ejecucion de una invovacion a un servicios
        /// </summary>
        /// <param name="instanceName">Nombre de la intancia del contador</param>
        /// <param name="ticks">El numero de ticks que demoro la operacion</param>
        public void IncrementInvocationExecutionCounterByOne( string instanceName, long ticks )
        {
            //Solo si estan habilitados utilizo los performance counters
            if ( !_enablePerformanceCounters )
                return;

            //Primero incremento el contador total
            _invocationsTotal.InstanceName = TotalName;
            _invocationsPerSecond.InstanceName = TotalName;
            _averageInvocationTotal.InstanceName = TotalName; //Incremento el contador con el tiempo consumido que se pasa por parametro
            _averageInvocationTotalBase.InstanceName = TotalName; //Incremento el contador base solo en uno
            _invocationsTotal.Increment( );
            _invocationsPerSecond.Increment( );
            _averageInvocationTotal.IncrementBy( ticks );
            _averageInvocationTotalBase.Increment( );
            
            //Despues configuro el nombre del servicio como nombre de la instancia e incremento los contadores
            _invocationsTotal.InstanceName = instanceName;
            _invocationsPerSecond.InstanceName = instanceName;
            _averageInvocationTotal.InstanceName = instanceName;
            _averageInvocationTotalBase.InstanceName = instanceName;
            _invocationsTotal.Increment( );
            _invocationsPerSecond.Increment( );
            _averageInvocationTotal.IncrementBy( ticks ); //Incremento el contador con el tiempo consumido que se pasa por parametro
            _averageInvocationTotalBase.Increment( ); //Incremento el contador base solo en uno
        }

        /// <summary>
        /// </summary>
        public static void StartStopWatch( )
        {
            _stopWatch.Reset( );
            _stopWatch.Start( );
        }

        /// <summary>
        /// </summary>
        public static long StopStopWatch( )
        {
            _stopWatch.Stop( );

            return _stopWatch.ElapsedTicks;
        }

        #endregion

        #region Dispose-Finalize Pattern

        /// <summary>
        /// Cuando se llama a ese metodo se produce la eliminacion del objeto en memoria
        /// </summary>
        public void Dispose( )
        {
            Dispose( true );
        }

        /// <summary>
        /// Cuando se llama a ese metodo se produce la eliminacion del objeto en memoria
        /// </summary>
        /// <param name="disposing">Si es True elimina de memoria los recursos no manejados y llama GC.SuppressFinalize( this )</param>
        protected virtual void Dispose( bool disposing )
        {
            if ( _invocationsTotal != null )
            {
                _invocationsTotal.Dispose( );
                _invocationsTotal = null;
            }
            if ( _invocationsPerSecond != null )
            {
                _invocationsPerSecond.Dispose( );
                _invocationsPerSecond = null;
            }
            if ( _averageInvocationTotal != null )
            {
                _averageInvocationTotal.Dispose( );
                _averageInvocationTotal = null;
            }
            if ( _averageInvocationTotalBase != null )
            {
                _averageInvocationTotalBase.Dispose( );
                _averageInvocationTotalBase = null;
            }

            if ( disposing )
                GC.SuppressFinalize( this );
        }

        /// <summary>
        /// Destructor de la clase
        /// </summary>
        ~PerformanceCountersManager( )
        {
            Dispose( false );
        }

        #endregion
    }
}